home *** CD-ROM | disk | FTP | other *** search
/ FishMarket 1.0 / FishMarket v1.0.iso / fishies / 301-325 / disk_325 / fam / minrexx.c < prev    next >
C/C++ Source or Header  |  1992-05-06  |  16KB  |  484 lines

  1. /*
  2.  *   This is an example of how REXX messages might be handled.    This is
  3.  *   a `minimum' example that both accepts asynchronous REXX messages and
  4.  *   can request REXX service.
  5.  *
  6.  *   Read this entire file!  It's short enough.
  7.  *
  8.  *   It is written in such a fashion that it can be attached to a program
  9.  *   with a minimum of fuss.  The only external symbols it makes available
  10.  *   are the seven functions and RexxSysBase.
  11.  *
  12.  *   This code is by Radical Eye Software, but it is put in the public
  13.  *   domain.  I would appreciate it if the following string was left in
  14.  *   both as a version check and as thanks from you for the use of this
  15.  *   code.
  16.  *
  17.  *   If you modify this file for your own use, don't bump the version
  18.  *   number; add a suffix, such as 1.0a or 1.0.3 or something, so we
  19.  *   don't have fake `versions' floating around.
  20.  */
  21. static char *blurb = "Radical Eye MinRexx 0.4" ;
  22.  
  23. /* includes added by DHN to get correct prototypes */
  24. #include "proto/exec.h"
  25. #include "string.h"
  26.  
  27. /*
  28.  *   We read in our own personal little include.
  29.  */
  30. #include "minrexx.h"
  31.  
  32. /*
  33.  * Added 01-Feb-90 by Darren New (new@udel.edu)
  34.  * If this is not NULL and we get a non-rexx message, pass it to
  35.  * this function to be handled. If this IS NULL, assume all messages
  36.  * are coming from REXX (crashing just as badly as before the change :-)
  37.  */
  38.  
  39. int (*HandleNonRexx)(struct Message *);
  40.  
  41. /*
  42.  *   All of our local globals, hidden from sight.
  43.  */
  44. static struct MsgPort *rexxPort ;       /* this is *our* rexx port */
  45. static int bringerdown ;           /* are we trying to shut down? */
  46. static struct rexxCommandList *globalrcl ; /* our command association list */
  47. static long stillNeedReplies ;           /* how many replies are pending? */
  48. static long rexxPortBit ;           /* what bit to wait on for Rexx? */
  49. static char *extension ;           /* the extension for macros */
  50. static int (*userdisp)(struct RexxMsg *, struct rexxCommandList *, STRPTR) ;
  51.                        /* the user's dispatch function */
  52. static struct RexxMsg *oRexxMsg ;       /* the outstanding Rexx message */
  53. /*
  54.  *   Our library base.    Don't you dare close this!
  55.  */
  56. struct RxsLib *RexxSysBase ;
  57. /*
  58.  *   This is the main entry point into this code.
  59.  */
  60. long __stdargs upRexxPort(s, rcl, exten, uf)
  61. /*
  62.  *   The first argument is the name of your port to be registered;
  63.  *   this will be used, for instance, with the `address' command of ARexx.
  64.  */
  65. char *s ;
  66. /*
  67.  *   The second argument is an association list of command-name/user-data
  68.  *   pairs.  It's an array of struct rexxCommandList, terminated by a
  69.  *   structure with a NULL in the name field. The commands are case
  70.  *   sensitive.  The user-data field can contain anything appropriate,
  71.  *   perhaps a function to call or some other data.
  72.  */
  73. struct rexxCommandList *rcl ;
  74. /*
  75.  *   The third argument is the file extension for ARexx macros invoked
  76.  *   by this program.  If you supply this argument, any `primitive' not
  77.  *   in the association list rcl will be sent out to ARexx for
  78.  *   interpretation, thus allowing macro programs to work just like
  79.  *   primitives.  If you do not want this behavior, supply a `NULL'
  80.  *   here, and those commands not understood will be replied with an
  81.  *   error value of RXERRORNOCMD.
  82.  */
  83. char *exten ;
  84. /*
  85.  *   The fourth argument is the user dispatch function.  This function
  86.  *   will *only* be called from rexxDisp(), either from the user calling
  87.  *   this function directly, or from dnRexxPort().  Anytime a command
  88.  *   match is found in the association list, this user-supplied function
  89.  *   will be called with two arguments---the Rexx message that was
  90.  *   received, and a pointer to the association pair.  This function
  91.  *   should return a `1' if the message was replied to by the function
  92.  *   and a `0' if the default success code of (0, 0) should be returned.
  93.  *   Note that the user function should never ReplyMsg() the message;
  94.  *   instead he should indicate the return values with replyRexxCmd();
  95.  *   otherwise we lose track of the messages that still lack replies.
  96.  */
  97. int (*uf)() ;
  98. /*
  99.  *   upRexxPort() returns the signal bit to wait on for Rexx messages.
  100.  *   If something goes wrong, it simply returns a `0'.  Note that this
  101.  *   function is safe to call multiple times because we check to make
  102.  *   sure we haven't opened already.  It's also a quick way to change
  103.  *   the association list or dispatch function.
  104.  */
  105. {
  106.    struct MsgPort *FindPort() ;
  107.    struct MsgPort *CreatePort() ;
  108.  
  109. /*
  110.  *   Some basic error checking.
  111.  */
  112.    if (rcl == NULL || uf == NULL)
  113.       return(0L) ;
  114. /*
  115.  *   If we aren't open, we make sure no one else has opened a port with
  116.  *   this name already.  If that works, and the createport succeeds, we
  117.  *   fill rexxPortBit with the value to return.
  118.  *
  119.  *   Note that rexxPortBit will be 0 iff rexxPort is NULL, so the check
  120.  *   for rexxPort == NULL also insures that our rexxPortBit is 0.
  121.  */
  122.    if (rexxPort == NULL) {
  123.       Forbid() ;
  124.       if (FindPort(s)==NULL)
  125.      rexxPort = CreatePort(s, 0L) ;
  126.       Permit() ;
  127.       if (rexxPort != NULL)
  128.      rexxPortBit = 1L << rexxPort->mp_SigBit ;
  129.    }
  130. /*
  131.  *   Squirrel away these values for our own internal access, and return
  132.  *   the wait bit.
  133.  */
  134.    globalrcl = rcl ;
  135.    extension = exten ;
  136.    userdisp = uf ;
  137.    return(rexxPortBit) ;
  138. }
  139. /*
  140.  *   This function closes the rexx library, but only if it is open
  141.  *   and we aren't expecting further replies from REXX.  It's
  142.  *   *private*, but it doesn't have to be; it's pretty safe to
  143.  *   call anytime.
  144.  */
  145. void __stdargs closeRexxLib(void);
  146. void __stdargs closeRexxLib() {
  147.    if (stillNeedReplies == 0 && RexxSysBase) {
  148.       CloseLibrary((struct Library *) RexxSysBase) ;
  149.       RexxSysBase = NULL ;
  150.    }
  151. }
  152. /*
  153.  *   This function closes down the Rexx port.  It is always safe to
  154.  *   call, and should *definitely* be made a part of your cleanup
  155.  *   routine.  No arguments and no return.  It removes the Rexx port,
  156.  *   replies to all of the messages and insures that we get replies
  157.  *   to all the ones we sent out, closes the Rexx library, deletes the
  158.  *   port, clears a few flags, and leaves.
  159.  */
  160. void __stdargs dnRexxPort() {
  161.    if (rexxPort) {
  162.       RemPort(rexxPort) ;
  163.       bringerdown = 1 ;
  164. /*
  165.  *   A message still hanging around?  We kill it off.
  166.  */
  167.       if (oRexxMsg) {
  168.      oRexxMsg->rm_Result1 = RXERRORIMGONE ;
  169.      ReplyMsg((struct Message *) oRexxMsg) ;
  170.      oRexxMsg = NULL ;
  171.       }
  172.       while (stillNeedReplies) {
  173.      WaitPort(rexxPort) ;
  174.      dispRexxPort() ;
  175.       }
  176.       closeRexxLib() ;
  177.       DeletePort(rexxPort) ;
  178.       rexxPort = NULL ;
  179.    }
  180.    rexxPortBit = 0 ;
  181. }
  182. /*
  183.  *   Here we dispatch any REXX messages that might be outstanding.
  184.  *   This is the main routine for handling Rexx messages.
  185.  *   This function is fast if no messages are outstanding, so it's
  186.  *   pretty safe to call fairly often.
  187.  *
  188.  *   If we are bring the system down and flushing messages, we reply
  189.  *   with a pretty serious return code RXERRORIMGONE.
  190.  *
  191.  *   No arguments, no returns.
  192.  */
  193. void __stdargs dispRexxPort() {
  194.    /* struct RexxMsg *GetMsg() ; */
  195.    extern __stdargs void DeleteArgstring(struct RexxArg *);
  196.    extern __stdargs void DeleteRexxMsg(struct RexxMsg *);
  197.    extern __stdargs long IsRexxMsg(struct RexxMsg *);
  198.    struct RexxMsg *RexxMsg ;
  199.    int __stdargs cmdcmp(char *, char *) ;
  200.    struct rexxCommandList *rcl ;
  201.    char *p ;
  202.    int dontreply ;
  203.  
  204. /*
  205.  *   If there's no rexx port, we're out of here.
  206.  */
  207.    if (rexxPort == NULL)
  208.       return ;
  209. /*
  210.  *   Otherwise we have our normal loop on messages.
  211.  */
  212.    while (RexxMsg = (struct RexxMsg *)GetMsg(rexxPort)) {
  213.  
  214. /*
  215.  *   See if the message is from REXX and handle it if not.
  216.  */
  217.  
  218.     if (HandleNonRexx && openRexxLib() && ! IsRexxMsg(RexxMsg)) {
  219.     (*HandleNonRexx)((struct Message *)RexxMsg);
  220.     continue;
  221.     }
  222.  
  223. /*
  224.  *   If we have a reply to a message we sent, we look at the second
  225.  *   argument.    If it's set, it's a function we are supposed to call
  226.  *   so we call it.  Then, we kill the argstring and the message
  227.  *   itself, decrement the outstanding count, and attempt to close
  228.  *   down the Rexx library.  Note that this call only succeeds if
  229.  *   there are no outstanding messages.  Also, it's pretty quick, so
  230.  *   don't talk to me about efficiency.
  231.  */
  232.       if (RexxMsg->rm_Node.mn_Node.ln_Type == NT_REPLYMSG) {
  233.      if (RexxMsg->rm_Args[1]) {
  234.         ((int (*)(struct RexxMsg *))(RexxMsg->rm_Args[1]))(RexxMsg) ;
  235.      }
  236.      DeleteArgstring((struct RexxArg *) RexxMsg->rm_Args[0]) ;
  237.      DeleteRexxMsg(RexxMsg) ;
  238.      stillNeedReplies-- ;
  239.      closeRexxLib() ;
  240. /*
  241.  *   The default case is we got a message and we need to check it for
  242.  *   primitives.  We skip past any initial tabs or spaces and initialize
  243.  *   the return code fields.
  244.  */
  245.       } else {
  246.      p = (char *)RexxMsg->rm_Args[0] ;
  247.      while (*p > 0 && *p <= ' ')
  248.         p++ ;
  249.      RexxMsg->rm_Result1 = 0 ;
  250.      RexxMsg->rm_Result2 = 0 ;
  251. /*
  252.  *   If somehow the reply is already done or postponed, `dontreply' is
  253.  *   set.
  254.  */
  255.      dontreply = 0 ;
  256. /*
  257.  *   If the sky is falling, we just blow up and replymsg.
  258.  */
  259.      if (bringerdown) {
  260.         RexxMsg->rm_Result1 = RXERRORIMGONE ;
  261. /*
  262.  *   Otherwise we cdr down our association list, comparing commands,
  263.  *   until we get a match.  If we get a match, we call the dispatch
  264.  *   function with the appropriate arguments, and break out.
  265.  */
  266.      } else {
  267.         oRexxMsg = RexxMsg ;
  268.         for (rcl = globalrcl; rcl->name; rcl++) {
  269.            if (cmdcmp(rcl->name, p) == 0) {
  270.           (*userdisp)(RexxMsg, rcl, p+strlen(rcl->name)) ;
  271.           break ;
  272.            }
  273.         }
  274. /*
  275.  *   If we broke out, rcl will point to the command we executed; if we
  276.  *   are at the end of the list, we didn't understand the command.  In
  277.  *   this case, if we were supplied an extension in upRexxPort, we know
  278.  *   that we should send the command out, so we do so, synchronously.
  279.  *   The synchronous send takes care of our reply.  If we were given a
  280.  *   NULL extension, we bitch that the command didn't make sense to us.
  281.  */
  282.         if (rcl->name == NULL) {
  283.            if (extension) {
  284.           syncRexxCmd(RexxMsg->rm_Args[0], RexxMsg) ;
  285.           dontreply = 1 ;
  286.            } else {
  287.           RexxMsg->rm_Result1 = RXERRORNOCMD ;
  288.            }
  289.         }
  290.      }
  291. /*
  292.  *   Finally, reply if appropriate.
  293.  */
  294.      oRexxMsg = NULL ;
  295.      if (! dontreply)
  296.         ReplyMsg((struct Message *) RexxMsg) ;
  297.       }
  298.    }
  299. }
  300. /*
  301.  *   This is the function we use to see if the command matches
  302.  *   the command string.  Not case sensitive, and the real command only
  303.  *   need be a prefix of the command string.  Make sure all commands
  304.  *   are given in lower case!
  305.  */
  306. static int __stdargs cmdcmp(char *c,char *m);
  307. static int __stdargs cmdcmp(c, m)
  308. register char *c, *m ;
  309. {
  310.    while (*c && ((*c == *m) || (*c == *m + 32 && ('a' <= *c && *c <= 'z')))) {
  311.       c++ ;
  312.       m++ ;
  313.    }
  314.    return (int) (*c) ;
  315. }
  316. /*
  317.  *   Opens the Rexx library if unopened.  Returns success (1) or
  318.  *   failure (0).  This is another function that is *private* but
  319.  *   that doesn't have to be.
  320.  */
  321. int __stdargs openRexxLib(void);
  322. int __stdargs openRexxLib() {
  323.  
  324.    if (RexxSysBase)
  325.       return(1) ;
  326.    return((RexxSysBase = (struct RxsLib *) OpenLibrary(RXSNAME, 0L)) != NULL) ;
  327. }
  328. /*
  329.  *   This is the general ARexx command interface, but is not the one
  330.  *   you will use most of the time; ones defined later are easier to
  331.  *   understand and use.  But they all go through here.
  332.  */
  333. struct RexxMsg *__stdargs sendRexxCmd(s, f, p1, p2, p3)
  334. char *s ;
  335. /*
  336.  *   The first parameter is the command to send to Rexx.
  337.  */
  338. int (*f)(struct RexxMsg *) ;
  339. /*
  340.  *   The second parameter is either NULL, indicating that the command
  341.  *   should execute asynchronously, or a function to be called when the
  342.  *   message we build up and send out here finally returns.  Please note
  343.  *   that the function supplied here could be called during cleanup after
  344.  *   a fatal error, so make sure it is `safe'.  This function always is
  345.  *   passed one argument, the RexxMsg that is being replied.
  346.  */
  347. STRPTR p1, p2, p3 ;
  348. /*
  349.  *   These are up to three arguments to be stuffed into the RexxMsg we
  350.  *   are building up, making the values available when the message is
  351.  *   finally replied to.  The values are stuffed into Args[2]..Args[4].
  352.  */
  353. {
  354.    struct RexxMsg *__stdargs CreateRexxMsg(struct MsgPort *,char*,char*) ;
  355.    STRPTR __stdargs CreateArgstring(char *,long) ;
  356.    register struct MsgPort *rexxport ;
  357.    register struct RexxMsg *RexxMsg ;
  358.  
  359. /*
  360.  *   If we have too many replies out there, we just return failure.
  361.  *   Note that you should check the return code to make sure your
  362.  *   message got out!  Then, we forbid, and make sure that:
  363.  *    - we have a rexx port open
  364.  *    - Rexx is out there
  365.  *    - the library is open
  366.  *    - we can create a message
  367.  *    - we can create an argstring
  368.  *
  369.  *   If all of these succeed, we stuff a few values and send the
  370.  *   message, permit, and return.
  371.  */
  372.    if (rexxPort == NULL || stillNeedReplies > MAXRXOUTSTANDING-1)
  373.       return(NULL) ;
  374.    RexxMsg = NULL ;
  375.    if (openRexxLib() && (RexxMsg =
  376.          CreateRexxMsg(rexxPort, extension, rexxPort->mp_Node.ln_Name)) &&
  377.          (RexxMsg->rm_Args[0] = CreateArgstring(s, (long)strlen(s)))) {
  378.       RexxMsg->rm_Action = RXCOMM ;
  379.       RexxMsg->rm_Args[1] = (STRPTR)f ;
  380.       RexxMsg->rm_Args[2] = p1 ;
  381.       RexxMsg->rm_Args[3] = p2 ;
  382.       RexxMsg->rm_Args[4] = p3 ;
  383.       RexxMsg->rm_Node.mn_Node.ln_Name = RXSDIR ;
  384.       Forbid() ;
  385.       if (rexxport = FindPort(RXSDIR))
  386.      PutMsg(rexxport, RexxMsg) ;
  387.       Permit() ;
  388.       if (rexxport) {
  389.      stillNeedReplies++ ;
  390.      return(RexxMsg) ;
  391.       } else
  392.      DeleteArgstring((struct RexxArgstring *)RexxMsg->rm_Args[0]) ;
  393.    }
  394.    if (RexxMsg)
  395.       DeleteRexxMsg(RexxMsg) ;
  396.    closeRexxLib() ;
  397.    return(NULL) ;
  398. }
  399. /*
  400.  *   This function is used to send out an ARexx message and return
  401.  *   immediately.  Its single parameter is the command to send.
  402.  */
  403. struct RexxMsg *__stdargs asyncRexxCmd(s)
  404. char *s ;
  405. {
  406.    return(sendRexxCmd(s, NULL, NULL, NULL, NULL)) ;
  407. }
  408. /*
  409.  *   This function sets things up to reply to the message that caused
  410.  *   it when we get a reply to the message we are sending out here.
  411.  *   But first the function we pass in, which actually handles the reply.
  412.  *   Note how we get the message from the Args[2]; Args[0] is the command,
  413.  *   Args[1] is this function, and Args[2]..Args[4] are any parameters
  414.  *   passed to sendRexxCmd() as p1..p3.  We pass the result codes right
  415.  *   along.
  416.  */
  417. static int __stdargs replytoit(struct RexxMsg *msg);
  418. static int __stdargs replytoit(msg)
  419. register struct RexxMsg *msg ;
  420. {
  421.    register struct RexxMsg *omsg ;
  422.  
  423.    omsg = (struct RexxMsg *)(msg->rm_Args[2]) ;
  424.    replyRexxCmd(omsg, msg->rm_Result1, msg->rm_Result2, NULL) ;
  425.    ReplyMsg((struct Message *) omsg) ;
  426.    return 0;
  427. }
  428. /*
  429.  *   This function makes use of everything we've put together so far,
  430.  *   and functions as a synchronous Rexx call; as soon as the macro
  431.  *   invoked here returns, we reply to `msg', passing the return codes
  432.  *   back.
  433.  */
  434. struct RexxMsg *__stdargs syncRexxCmd(s, msg)
  435. char *s ;
  436. struct RexxMsg *msg ;
  437. {
  438.    return(sendRexxCmd(s, &replytoit, (STRPTR) msg, NULL, NULL)) ;
  439. }
  440. /*
  441.  *   There are times when you want to pass back return codes or a
  442.  *   return string; call this function when you want to do that,
  443.  *   and return `1' from the user dispatch function so the main
  444.  *   event loop doesn't reply (because we reply here.)  This function
  445.  *   always returns 1.
  446.  */
  447. void __stdargs replyRexxCmd(msg, primary, secondary, string)
  448. /*
  449.  *   The first parameter is the message we are replying to.
  450.  */
  451. struct RexxMsg *msg ;
  452. /*
  453.  *   The next two parameters are the primary and secondary return
  454.  *   codes.
  455.  */
  456. long primary, secondary ;
  457. /*
  458.  *   The final parameter is a return string.  This string is only
  459.  *   returned if the primary return code is 0, and a string was
  460.  *   requested.
  461.  *
  462.  *   We also note that we have replied to the message that came in.
  463.  */
  464. char *string ;
  465. {
  466.    STRPTR __stdargs CreateArgstring(char *, long) ;
  467.  
  468. /*
  469.  *   Note how we make sure the Rexx Library is open before calling
  470.  *   CreateArgstring . . . and we close it down at the end, if possible.
  471.  */
  472.    if (primary == 0 && (msg->rm_Action & (1L << RXFB_RESULT))) {
  473.       if (string && openRexxLib())
  474.      secondary = (long)CreateArgstring(string, (long)strlen(string)) ;
  475.       else
  476.      secondary = 0L ;
  477.    }
  478.    msg->rm_Result1 = primary ;
  479.    msg->rm_Result2 = secondary ;
  480.    closeRexxLib() ;
  481. }
  482.  
  483.  
  484.